#include<stdio.h>
#include<unistd.h>

int global_value = 100;
//进程 = 内核数据结构+进程对应的代码和数据

//进程地址空间(虚拟地址,并不是物理地址)  --进程的独立性 
//操作系统给进程画的大饼(每个进程都以为自己独享所有的空间)
//struct 蓝图1、2、3：记录每个进程画的饼{char* who;char* when;char* tager.....}进程要被管理，饼也要被管理(都是先描述再组织)
//地址空间的本质:是内核的一种数据结构！mm_struct
//低->高   代码区-已初始化-未初始化-heap-共享区-stack  --mm_struct{各种区的stat\end}  --code stat;code end; 如果main函数地址是0x1122 那么code stat就是0x1122；cpu就能通过code stat 直接找到我们的main函数
//页表不知是做映射，非法访问会拦截你这个进程(所有进程都要遵守)


//为什么要有地址空间？
//1.如果让进程之间访问物理内存，万一进程越界非法操作呢？-----非常不安全
//2.地址空间的存在，可以更方便的进行进程和进程的数据代码的解耦，保证进程独立性这样的特征
//3.


//1.可执行程序里面，也有地址(逻辑地址)
//2.虚拟地址空间，OS要遵守，编译器也要遵守(编译器编译我的代码的时候，就是按照虚拟地址空间的方式进程对我的代码和数据进程编址的！)    ---比如说我的程序会分成什么区什么区(就有了虚拟地址)，把程序的代码放到物理内存(就具有了物理地址)，物理内存就有了main函数等函数，cpu通过mm_strut{code stat}页表查到main函数，然后main函数要执行其他函数时，其他函数的地址传给cpu里面的寄存器，cpu通过过页表找到其他的函数执行其他函数。  
//3.让进程以统一的视角，来看待进程对应的代码和各个区域，方便使用          编译器也已统一的视角来进行编译代码
int main()
{
  pid_t id = fork();//父子进程都会跑，但是谁先跑不知道
  if(id < 0)
  {
    printf("fork error\n");
    return 1;
  }
  else if(id == 0)//每个进程都有自己的地址空间和页表
  {
    int cnt = 0;
    while(1)
    {//进程都无法看到物理内存，所以每个进程都以为能独占系统空间(虚拟内存)：保证进程的独立性   ---通过地址空间，通过页表，让不同的进程，映射到并不同的物理内存空间
    printf("我是子进程，pid:%d,ppid:%d | global_value:%d\n",getpid(),getppid(),global_value);//虚拟地址通过页表映射找到物理内存
    sleep(1);
    cnt++;
    if(cnt == 10)
    {
      global_value = 300;
      printf("子进程已经修改了全局变量啦。。。。。。。");//当改变全局变量时,会发生----写时拷贝(操作系统自动做的)       任何一方尝试写入,OS先进行数据拷贝，更改页表映射,然后再让进程进行修改
    }//因为进程具有独立性，一个进程对被共享的数据做修改，如果影响了其他进程，不能称之为独立性
  
    }
  }
  else
  {
    while(1)
    {
    printf("我是父进程，pid:%d,ppid:%d | global_value:%d\n",getpid(),getppid(),global_value);
    sleep(2);
    }
  }
  return 0;
}
